package com.boot2.core.utils;

import com.boot2.core.cache.redis.RedisDao;
import com.boot2.core.exception.TokenValidationException;
import com.boot2.core.web.filter.JwtAuthenticationFilter;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class JwtUtil {
    public static final long EXPIRATION_TIME = 3600_000_000L; // 1000 hour
    public static final String SECRET = "ThisIsASecret";// please change to your own encryption secret.
    public static final String TOKEN_PREFIX = "Bearer";
    /**
     * token不互踢
     */
    public static final String TOKEN_NOT_KICK = "_kick";
    // public static final String X_ACCESS_TOKEN = "Authorization";
    public static final String X_ACCESS_TOKEN = "x-access-token";
    public static final String USER_ID = "userId";
    public static final String USER = "user";
    public static final String REDIS_LOGIN_TOKEN_PREFIX = "user:login:token:";
    public static final String REDIS_LOGIN_USERID_PREFIX = "user:login:userId:";

    private static final Logger logger = LoggerFactory.getLogger(JwtUtil.class);
    /**
     * 校验取值时用的key,默认为userId
     */
    public static String checkKey = USER_ID;
    private static RedisDao redisDao;

    /**
     * 过滤策略，支持三种模式，纯redis,纯jwt, redisJwt(默认值)
     */
    private static String filterStrategy = FilterStrategy.redisJwt.name();

    public static void setRedisDao(RedisDao redisDao) {
        JwtUtil.redisDao = redisDao;
    }

    public static void setFilterStrategy(String filterStrategy) {
        JwtUtil.filterStrategy = filterStrategy;
    }

    /**
     * 放对象的id
     *
     * @param str
     * @return
     */
    public static String generateToken(Serializable str) {
        HashMap<String, Object> map = new HashMap<>();
        // you can put any data in the map
        try {
            map.put(USER, str);
        } catch (Exception e) {
            logger.warn("Encryption failed. " + e.getMessage());
            throw new RuntimeException("Encryption failed");
        }
        String jwt = Jwts.builder().setClaims(map).setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                .signWith(SignatureAlgorithm.HS512, SECRET).compact();
        return jwt;
    }

    /**
     * 放对象
     *
     * @param obj
     * @return
     */
    public static String generateToken(Object obj) {
        return generateToken(USER, obj);
    }

    /**
     * 放对象
     *
     * @param key
     * @param obj
     * @return
     */
    public static String generateToken(String key, Object obj) {
        return generateToken(key, obj, EXPIRATION_TIME);
    }

    /**
     * 放对象
     *
     * @param key
     * @param obj
     * @return
     */
    public static String generateToken(String key, Object obj, long expirationTime) {
        HashMap<String, Object> map = new HashMap<>();
        // you can put any data in the map
        try {
            map.put(key, obj);
        } catch (Exception e) {
            logger.warn("Encryption failed. " + e.getMessage());
            throw new RuntimeException("Encryption failed");
        }
        String jwt = Jwts.builder().setClaims(map).setExpiration(new Date(System.currentTimeMillis() + expirationTime))
                .signWith(SignatureAlgorithm.HS512, SECRET).compact();
        return jwt;
    }

    public static HttpServletRequest validateTokenAndAddUserIdToHeader(HttpServletRequest request) {
        return validateTokenAndAddUserIdToHeader(request, null, null);
    }

    public static HttpServletRequest validateTokenAndAddUserIdToHeader(HttpServletRequest request, String servletPath, List<String> noLoginNeedUserInfoList) {
        //token支持传header和param
//        String token = request.getHeader(X_ACCESS_TOKEN);
//        if (StringUtils.isEmptyString(token)) {
//            token = request.getParameter(X_ACCESS_TOKEN);
//        }
        String token = ServletUtil.getRequestToken(request);
//        System.out.println("\n\n\n\ntoken = " + token);
        //不管是否拦截，只要传了token就走token逻辑
        if (StringUtils.isNotEmpty(token)) {
            // parse the token.
            Object obj = null;
            try {
                obj = getByToken(token,request);
//                System.out.println("checkKey = " + checkKey);
//                System.out.println("obj = " + obj);

                return new CustomHttpServletRequest(request, obj);
            } catch (Exception e) {
                e.printStackTrace();
//                logger.error(e.getMessage());
                throw new TokenValidationException(e.getMessage());
            }
        } else {
//            //如果没有传token,也没传需要读取用户信息的url
//            if (noLoginNeedUserInfoList == null || !noLoginNeedUserInfoList.contains(servletPath)) {
//                logger.info("token can not be null ");
//                throw new TokenValidationException("token can not be null ");
//            }
            //如果是要获取用户信息，如果token为空,则仍应该为null,或直接返回原始request即可
//            return new CustomHttpServletRequest(request, null);
            Boolean isNeedToken = (Boolean) request.getAttribute(JwtAuthenticationFilter.class.getName() + "_isNeedToken");
            if (null != isNeedToken && isNeedToken) {
                throw new TokenValidationException("token can not be null");
            }
            return request;
        }
    }

    /**
     * 通过token获取用户id
     *
     * @param token
     * @return
     */
    public static Object getByToken(String token,HttpServletRequest request) {
        //先从redis中获取
        Object obj = null;
        if (filterStrategy.equalsIgnoreCase(FilterStrategy.redis.name()) || filterStrategy.equalsIgnoreCase(FilterStrategy.redisJwt.name())) {
            if (redisDao != null) {
                obj = redisDao.get(REDIS_LOGIN_TOKEN_PREFIX + token);
//                if (!token.endsWith(TOKEN_NOT_KICK)) {
//                    String fullUrl = null;
//                    if (null!=request) {
//                        fullUrl = request.getRequestURI();
//                    }
//                    System.out.println(", fullUrl = " + fullUrl + " 此时token: " + token);
//                    HlAssert.notNull(obj, "当前用户未登录或已退出");
//                }
            }
        }

        if (filterStrategy.equalsIgnoreCase(FilterStrategy.jwt.name()) || filterStrategy.equalsIgnoreCase(FilterStrategy.redisJwt.name())) {
            //如果redis中不存在，直接通过jwt解析
            if (null == obj) {
                token = token.replace(TOKEN_NOT_KICK, "");
                Map<String, Object> body = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token.replace(TOKEN_PREFIX, "")).getBody();
                obj = body.get(checkKey);
            }
        }
        return obj;
    }

    /**
     * 通过token获取用户id
     *
     * @param token
     * @return
     */
    public static Object getByToken(String token) {
        //先从redis中获取
        Object obj = null;
        if (filterStrategy.equalsIgnoreCase(FilterStrategy.redis.name()) || filterStrategy.equalsIgnoreCase(FilterStrategy.redisJwt.name())) {
            if (redisDao != null) {
                obj = redisDao.get(REDIS_LOGIN_TOKEN_PREFIX + token);
//                if (!token.endsWith(TOKEN_NOT_KICK)) {
//                    String fullUrl = null;
//                    System.out.println(", fullUrl = " + fullUrl + " 此时token: " + token);
//                    HlAssert.notNull(obj, "当前用户未登录或已退出");
//                }
            }
        }

        if (filterStrategy.equalsIgnoreCase(FilterStrategy.jwt.name()) || filterStrategy.equalsIgnoreCase(FilterStrategy.redisJwt.name())) {
            //如果redis中不存在，直接通过jwt解析
            if (null == obj) {
                token = token.replace(TOKEN_NOT_KICK, "");
                Map<String, Object> body = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token.replace(TOKEN_PREFIX, "")).getBody();
                obj = body.get(checkKey);
            }
        }
        return obj;
    }

    public static enum FilterStrategy {
        //同时redis和jwt,先redis后jwt
        redisJwt,
        //仅仅redis
        redis,
        //仅仅jwt
        jwt,
    }

    public static class CustomHttpServletRequest extends HttpServletRequestWrapper {
        private Object obj;
        private String publicKey;

        public CustomHttpServletRequest(HttpServletRequest request, Object obj) {
            super(request);
            this.obj = obj;
        }

        @Override
        public Enumeration<String> getHeaders(String name) {
            if (checkKey.equals(name) && null != obj) {
                return Collections.enumeration(Arrays.asList(obj + ""));
            }
            return super.getHeaders(name);
        }

        @Override
        public String getHeader(String name) {
            if (checkKey.equals(name) && null != obj) {
                return obj.toString();
            }
            return super.getHeader(name);
        }
    }


}